import { jest } from '@jest/globals';
import RoomManager from '../../services/RoomManager.js';
import { mockLogger, testUtils, testDbConfig } from '../setup.js';

// 模拟数据库服务
const createMockDatabaseService = () => ({
  createRoom: jest.fn(),
  getRoomById: jest.fn(),
  getAvailableRooms: jest.fn(),
  joinRoom: jest.fn(),
  leaveRoom: jest.fn(),
  updatePlayerReady: jest.fn(),
  updateRoomStatus: jest.fn(),
  updatePlayerStatus: jest.fn(),
  cleanupExpiredData: jest.fn()
});

describe('RoomManager', () => {
  let roomManager;
  let mockDb;

  beforeEach(() => {
    mockDb = createMockDatabaseService();
    roomManager = new RoomManager(mockDb);
    // 模拟logger
    roomManager.logger = mockLogger;
  });

  afterEach(() => {
    jest.clearAllMocks();
  });

  describe('createRoom', () => {
    it('should create room successfully with valid inputs', async () => {
      // 准备测试数据
      const roomName = 'Test Room';
      const hostPlayerName = 'TestHost';
      
      mockDb.createRoom.mockResolvedValue(true);

      // 执行测试
      const result = await roomManager.createRoom(roomName, hostPlayerName);

      // 验证结果
      expect(result.success).toBe(true);
      expect(result.data).toHaveProperty('room');
      expect(result.data).toHaveProperty('player');
      expect(result.data.room.name).toBe(roomName);
      expect(result.data.player.name).toBe(hostPlayerName);
      expect(result.data.player.isHost).toBe(true);

      // 验证数据库调用
      expect(mockDb.createRoom).toHaveBeenCalledWith(
        expect.objectContaining({
          name: roomName,
          hostName: hostPlayerName,
          status: 'waiting',
          playerCount: 1,
          maxPlayers: 3
        })
      );

      // 验证内存状态
      expect(roomManager.rooms.size).toBe(1);
      expect(roomManager.playerRooms.size).toBe(1);
    });

    it('should reject invalid room name', async () => {
      const result = await roomManager.createRoom('', 'TestHost');
      
      expect(result.success).toBe(false);
      expect(result.message).toContain('房间名称');
      expect(mockDb.createRoom).not.toHaveBeenCalled();
    });

    it('should reject invalid player name', async () => {
      const result = await roomManager.createRoom('Test Room', '');
      
      expect(result.success).toBe(false);
      expect(result.message).toContain('玩家名称');
      expect(mockDb.createRoom).not.toHaveBeenCalled();
    });

    it('should handle database errors gracefully', async () => {
      mockDb.createRoom.mockRejectedValue(new Error('Database error'));

      const result = await roomManager.createRoom('Test Room', 'TestHost');

      expect(result.success).toBe(false);
      expect(result.message).toBe('服务器内部错误');
    });
  });

  describe('joinRoom', () => {
    const mockExistingRoom = {
      id: 'TEST01',
      name: 'Test Room',
      status: 'waiting',
      player_count: 1,
      max_players: 3,
      players: [
        { id: 'host-id', name: 'Host', seatIndex: 0, isReady: false, status: 'online' }
      ]
    };

    beforeEach(() => {
      mockDb.getRoomById.mockResolvedValue(mockExistingRoom);
      mockDb.joinRoom.mockResolvedValue(true);
    });

    it('should join room successfully', async () => {
      const updatedRoom = {
        ...mockExistingRoom,
        players: [
          ...mockExistingRoom.players,
          { id: 'new-player-id', name: 'NewPlayer', seatIndex: 1, isReady: false, status: 'online' }
        ]
      };
      mockDb.getRoomById.mockResolvedValueOnce(mockExistingRoom)
                        .mockResolvedValueOnce(updatedRoom);

      const result = await roomManager.joinRoom('TEST01', 'NewPlayer');

      expect(result.success).toBe(true);
      expect(result.data.player.name).toBe('NewPlayer');
      expect(result.data.player.isHost).toBe(false);
      expect(mockDb.joinRoom).toHaveBeenCalled();
    });

    it('should reject joining non-existent room', async () => {
      mockDb.getRoomById.mockResolvedValue(null);

      const result = await roomManager.joinRoom('INVALID', 'NewPlayer');

      expect(result.success).toBe(false);
      expect(result.message).toBe('房间不存在');
    });

    it('should reject joining full room', async () => {
      const fullRoom = {
        ...mockExistingRoom,
        player_count: 3,
        players: new Array(3).fill().map((_, i) => ({
          id: `player-${i}`,
          name: `Player${i}`,
          seatIndex: i,
          isReady: false,
          status: 'online'
        }))
      };
      mockDb.getRoomById.mockResolvedValue(fullRoom);

      const result = await roomManager.joinRoom('TEST01', 'NewPlayer');

      expect(result.success).toBe(false);
      expect(result.message).toBe('房间已满');
    });

    it('should reject joining started game', async () => {
      const startedRoom = { ...mockExistingRoom, status: 'playing' };
      mockDb.getRoomById.mockResolvedValue(startedRoom);

      const result = await roomManager.joinRoom('TEST01', 'NewPlayer');

      expect(result.success).toBe(false);
      expect(result.message).toBe('房间游戏已开始，无法加入');
    });

    it('should reject duplicate player names', async () => {
      const result = await roomManager.joinRoom('TEST01', 'Host');

      expect(result.success).toBe(false);
      expect(result.message).toBe('该名称已被使用');
    });
  });

  describe('leaveRoom', () => {
    beforeEach(() => {
      // 设置一个玩家在房间中的状态
      roomManager.playerRooms.set('test-player-1', 'TEST01');
      roomManager.rooms.set('TEST01', testUtils.createMockRoom({
        id: 'TEST01',
        players: [testUtils.createMockPlayer({ id: 'test-player-1' })]
      }));
    });

    it('should leave room successfully', async () => {
      mockDb.leaveRoom.mockResolvedValue(0); // 房间为空

      const result = await roomManager.leaveRoom('test-player-1');

      expect(result.success).toBe(true);
      expect(result.roomDeleted).toBe(true);
      expect(roomManager.playerRooms.has('test-player-1')).toBe(false);
      expect(roomManager.rooms.has('TEST01')).toBe(false);
    });

    it('should handle non-empty room after leaving', async () => {
      mockDb.leaveRoom.mockResolvedValue(2); // 还有2个玩家
      mockDb.getRoomById.mockResolvedValue(testUtils.createMockRoom());

      const result = await roomManager.leaveRoom('test-player-1');

      expect(result.success).toBe(true);
      expect(result.roomDeleted).toBeFalsy();
      expect(result.data).toHaveProperty('room');
    });

    it('should reject leaving when not in room', async () => {
      const result = await roomManager.leaveRoom('non-existent-player');

      expect(result.success).toBe(false);
      expect(result.message).toBe('您不在任何房间中');
    });
  });

  describe('toggleReady', () => {
    beforeEach(() => {
      const mockRoom = testUtils.createMockRoom({
        hostId: 'host-id',
        players: [
          { id: 'host-id', name: 'Host', isReady: false },
          { id: 'player-1', name: 'Player1', isReady: false }
        ]
      });
      
      roomManager.playerRooms.set('player-1', 'TEST01');
      roomManager.rooms.set('TEST01', mockRoom);
      mockDb.updatePlayerReady.mockResolvedValue(true);
    });

    it('should toggle ready state successfully', async () => {
      const result = await roomManager.toggleReady('player-1', true);

      expect(result.success).toBe(true);
      expect(mockDb.updatePlayerReady).toHaveBeenCalledWith('player-1', 'TEST01', true);
      
      // 验证内存状态更新
      const room = roomManager.rooms.get('TEST01');
      const player = room.players.find(p => p.id === 'player-1');
      expect(player.isReady).toBe(true);
    });

    it('should reject ready toggle for host', async () => {
      roomManager.playerRooms.set('host-id', 'TEST01');

      const result = await roomManager.toggleReady('host-id', true);

      expect(result.success).toBe(false);
      expect(result.message).toBe('房主无需准备，请直接开始游戏');
    });

    it('should reject ready toggle when game started', async () => {
      const room = roomManager.rooms.get('TEST01');
      room.status = 'playing';

      const result = await roomManager.toggleReady('player-1', true);

      expect(result.success).toBe(false);
      expect(result.message).toBe('游戏已开始，无法更改准备状态');
    });
  });

  describe('canStartGame', () => {
    it('should allow starting with 3 ready players', () => {
      const room = testUtils.createMockRoom({
        hostId: 'host-id',
        status: 'waiting',
        players: [
          { id: 'host-id', name: 'Host', isReady: false },
          { id: 'player-1', name: 'Player1', isReady: true },
          { id: 'player-2', name: 'Player2', isReady: true }
        ]
      });
      
      roomManager.rooms.set('TEST01', room);

      const result = roomManager.canStartGame('TEST01');

      expect(result.canStart).toBe(true);
    });

    it('should reject starting with insufficient players', () => {
      const room = testUtils.createMockRoom({
        players: [
          { id: 'host-id', name: 'Host', isReady: false },
          { id: 'player-1', name: 'Player1', isReady: true }
        ]
      });
      
      roomManager.rooms.set('TEST01', room);

      const result = roomManager.canStartGame('TEST01');

      expect(result.canStart).toBe(false);
      expect(result.message).toBe('玩家人数不足，需要3人');
    });

    it('should reject starting with unready players', () => {
      const room = testUtils.createMockRoom({
        hostId: 'host-id',
        players: [
          { id: 'host-id', name: 'Host', isReady: false },
          { id: 'player-1', name: 'Player1', isReady: false },
          { id: 'player-2', name: 'Player2', isReady: true }
        ]
      });
      
      roomManager.rooms.set('TEST01', room);

      const result = roomManager.canStartGame('TEST01');

      expect(result.canStart).toBe(false);
      expect(result.message).toBe('还有玩家未准备');
    });
  });

  describe('startGame', () => {
    beforeEach(() => {
      const room = testUtils.createMockRoom({
        hostId: 'host-id',
        status: 'waiting',
        players: [
          { id: 'host-id', name: 'Host', isReady: false },
          { id: 'player-1', name: 'Player1', isReady: true },
          { id: 'player-2', name: 'Player2', isReady: true }
        ]
      });
      
      roomManager.playerRooms.set('host-id', 'TEST01');
      roomManager.rooms.set('TEST01', room);
      mockDb.updateRoomStatus.mockResolvedValue(true);
    });

    it('should start game successfully', async () => {
      const result = await roomManager.startGame('host-id');

      expect(result.success).toBe(true);
      expect(mockDb.updateRoomStatus).toHaveBeenCalledWith('TEST01', 'playing');
      
      const room = roomManager.rooms.get('TEST01');
      expect(room.status).toBe('playing');
    });

    it('should reject non-host starting game', async () => {
      roomManager.playerRooms.set('player-1', 'TEST01');

      const result = await roomManager.startGame('player-1');

      expect(result.success).toBe(false);
      expect(result.message).toBe('只有房主可以开始游戏');
    });
  });

  describe('updatePlayerStatus', () => {
    beforeEach(() => {
      const room = testUtils.createMockRoom({
        players: [testUtils.createMockPlayer({ id: 'test-player' })]
      });
      
      roomManager.playerRooms.set('test-player', 'TEST01');
      roomManager.rooms.set('TEST01', room);
      mockDb.updatePlayerStatus.mockResolvedValue(true);
    });

    it('should update player status successfully', async () => {
      const result = await roomManager.updatePlayerStatus('test-player', 'offline', 'socket123');

      expect(result).toBe(true);
      expect(mockDb.updatePlayerStatus).toHaveBeenCalledWith(
        'test-player', 'TEST01', 'offline', 'socket123'
      );
      
      const room = roomManager.rooms.get('TEST01');
      const player = room.players.find(p => p.id === 'test-player');
      expect(player.status).toBe('offline');
    });

    it('should handle player not in room', async () => {
      const result = await roomManager.updatePlayerStatus('non-existent', 'offline');

      expect(result).toBe(false);
      expect(mockDb.updatePlayerStatus).not.toHaveBeenCalled();
    });
  });

  describe('cleanup', () => {
    it('should cleanup expired rooms and database', async () => {
      mockDb.cleanupExpiredData.mockResolvedValue(true);

      await roomManager.cleanup();

      expect(mockDb.cleanupExpiredData).toHaveBeenCalled();
    });

    it('should handle cleanup errors gracefully', async () => {
      mockDb.cleanupExpiredData.mockRejectedValue(new Error('Cleanup failed'));

      await roomManager.cleanup();

      // 应该不抛出错误
      expect(mockDb.cleanupExpiredData).toHaveBeenCalled();
    });
  });

  describe('getStats', () => {
    it('should return correct statistics', () => {
      // 设置一些测试数据
      roomManager.rooms.set('room1', { status: 'waiting' });
      roomManager.rooms.set('room2', { status: 'playing' });
      roomManager.playerRooms.set('player1', 'room1');
      roomManager.playerRooms.set('player2', 'room2');

      const stats = roomManager.getStats();

      expect(stats).toEqual({
        totalRooms: 2,
        totalPlayers: 2,
        playingRooms: 1,
        waitingRooms: 1
      });
    });
  });
});